/*
 *	ME2 C++ SDK
 *	Copyright (c) 2010 _silencer
 *
 *	Permission is hereby granted, free of charge, to any person
 *	obtaining a copy of this software and associated documentation
 *	files (the "Software"), to deal in the Software without
 *	restriction, including without limitation the rights to use,
 *	copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the
 *	Software is furnished to do so, subject to the following
 *	conditions:
 *
 *	The above copyright notice and this permission notice shall be
 *	included in all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *	OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef UNREAL_SDK_GENERATOR_H
#define UNREAL_SDK_GENERATOR_H

#include "unreal_sdk.h"

namespace unreal
{
	struct usdk_variable_info
	{
		usdk_variable_info()
			: element_size( 0 ), element_count( 0 ), offset( 0 ), is_return_value( false )
		{
		}

		usdk_variable_info( uproperty* variable )
		{
			name = variable->get_name();
			type = variable->get_type();
			element_size = variable->get_element_size();
			element_count = variable->get_array_dimension();
			offset = variable->get_offset();

			/* Property flags still not correct? */
			/*( ( variable->get_property_flags() & uproperty_return_parm ) == 0 )*/

			/* Fall back to easy way :) */
			is_return_value = ( name == "ReturnValue" );
		}

		std::string name;
		uproperty::type_info type;
		uint32 element_size;
		uint32 element_count;
		uint32 offset;
		bool is_return_value;
	};

	typedef boost::shared_ptr<usdk_variable_info> usdk_variable_info_ptr;
	typedef std::vector<usdk_variable_info_ptr> usdk_variable_list;
	typedef std::vector<usdk_variable_info_ptr>::iterator usdk_variable_list_iterator;

	struct usdk_function_info
	{
		std::string name;
		std::string full_name;
		std::vector<usdk_variable_info_ptr> variables;
		usdk_variable_info_ptr return_value;
	};

	typedef boost::shared_ptr<usdk_function_info> usdk_function_info_ptr;
	typedef std::vector<usdk_function_info_ptr> usdk_function_list;
	typedef std::vector<usdk_function_info_ptr>::iterator usdk_function_list_iterator;

	typedef std::vector<std::string> usdk_unknown_list;
	typedef std::vector<std::string>::iterator usdk_unknown_list_iterator;

	typedef stdext::hash_set<std::string> usdk_dependency_list;
	typedef stdext::hash_set<std::string>::iterator usdk_dependency_list_iterator;

	struct usdk_topological_concept
	{
		typedef int implements_topological_concept;

		std::string name;
		usdk_dependency_list dependencies;
	};

	struct usdk_structure_info: public usdk_topological_concept
	{
		usdk_structure_info()
			: is_state( false )
		{
		}

		//std::string name;
		std::vector<usdk_variable_info_ptr> variables;
		usdk_unknown_list unknowns;
		bool is_state;
		//usdk_dependency_list dependencies;
	};

	typedef boost::shared_ptr<usdk_structure_info> usdk_structure_info_ptr;
	typedef std::vector<usdk_structure_info_ptr> usdk_structure_list;
	typedef std::vector<usdk_structure_info_ptr>::iterator usdk_structure_list_iterator;

	struct usdk_constant_info
	{
		std::string name;
		std::string value;
	};

	typedef boost::shared_ptr<usdk_constant_info> usdk_constant_info_ptr;
	typedef std::vector<usdk_constant_info_ptr> usdk_constant_list;
	typedef std::vector<usdk_constant_info_ptr>::iterator usdk_constant_list_iterator;

	struct usdk_enumeration_info
	{
		std::string name;
		usdk_constant_list values;
	};

	typedef boost::shared_ptr<usdk_enumeration_info> usdk_enumeration_info_ptr;
	typedef std::vector<usdk_enumeration_info_ptr> usdk_enumeration_list;
	typedef std::vector<usdk_enumeration_info_ptr>::iterator usdk_enumeration_list_iterator;

	struct usdk_class_info: public usdk_topological_concept
	{
		usdk_class_info()
			: processed( false )
		{
		}

		bool processed;
		//std::string name;
		std::string super;
		usdk_function_list functions;
		usdk_variable_list variables;
		usdk_structure_list structures;
		usdk_enumeration_list enumerations;
		usdk_constant_list constants;
		usdk_unknown_list unknowns;
		std::string package;
		//usdk_dependency_list dependencies;
	};

	typedef boost::shared_ptr<usdk_class_info> usdk_class_info_ptr;
	typedef std::vector<usdk_class_info_ptr> usdk_class_list;
	typedef std::vector<usdk_class_info_ptr>::iterator usdk_class_list_iterator;

	typedef stdext::hash_map<std::string, usdk_class_info_ptr> usdk_class_map;
	typedef stdext::hash_map<std::string, usdk_class_info_ptr>::iterator usdk_class_map_iterator;

	template<class _T>
	class topological_sorter
	{
	public:

		typedef boost::shared_ptr<_T> type_ptr; 
		typedef std::vector<type_ptr> type_list;
		typedef typename type_list::iterator type_list_iterator;
		typename _T::implements_topological_concept needs_topological_concept;

		topological_sorter( type_list& list )
			: input_list_( list )
		{
		}

		void topological_sorter::sort()
		{
			visited_.clear();

			type_list_iterator it( input_list_.begin() );

			for ( ; it != input_list_.end(); ++it )
				visit( it );

			input_list_ = output_list_;
		}

	private:

		void visit( type_list_iterator item )
		{
			if ( (*item).get() )
			{
				if ( !visited_[(*item)->name] )
				{
					visited_[(*item)->name] = true;

					type_list_iterator it( input_list_.begin() );

					for ( ; it != input_list_.end(); ++it )
					{
						if ( !(*it).get() )
							continue;

						/* Fix object topological sort */
						if ( (*item)->name == "Object" )
							continue;

						usdk_dependency_list_iterator dependency_search( (*item)->dependencies.find( (*it)->name ) );
						bool depends_on( dependency_search != (*item)->dependencies.end() );

						if ( depends_on )
							visit( it );
					}

					output_list_.push_back( *item );
				}
			}
		}

		type_list& input_list_;
		type_list output_list_;
		stdext::hash_map<std::string, bool> visited_;
	};

	struct usdk_package_info
	{
		usdk_class_list classes;
		usdk_class_map classes_map;
	};

	typedef boost::shared_ptr<usdk_package_info> usdk_package_info_ptr;
	typedef stdext::hash_map<std::string, usdk_package_info_ptr> usdk_package_list;
	typedef stdext::hash_map<std::string, usdk_package_info_ptr>::iterator usdk_package_list_iterator;

	class usdk_collector
	{
	public:

		inline void process_all()
		{
			process_range( 0, uobject::table_->size() );
		}

		inline void process_range( uint32 begin, uint32 end )
		{
			for ( uint32 index( 0 ); index < uobject::table_->size(); ++index )
				process( index );
		}

		void process( uint32 index );
		void sort();

		usdk_class_list& get_classes()
		{
			return classes_;
		}

		usdk_package_list& get_packages()
		{
			return packages_;
		}

	private:

		usdk_class_list classes_;
		usdk_class_map classes_map_;
		usdk_package_list packages_;
	};

	class usdk_generator
	{
	public:

		usdk_generator( usdk_collector& collector )
			: collector_( collector )
		{
		}

		virtual ~usdk_generator(){}

		virtual void build_header( std::ostream& output, std::string package = "" ) = 0;
		virtual void build_implementation( std::ostream& output, std::string package = "" ) = 0;

	protected:

		usdk_collector& collector_;
	};

	class unative_sdk_generator: public usdk_generator
	{
	public:

		unative_sdk_generator( usdk_collector& collector )
			: usdk_generator( collector )
		{
		}

		void build_global_header( std::ostream& output );
		void build_header( std::ostream& output, std::string package = "" );
		void build_implementation( std::ostream& output, std::string package = "" );
	};

	class uscript_sdk_generator: public usdk_generator
	{
	public:

		void build_header( std::ostream& output, std::string package = "" ){}
		void build_implementation( std::ostream& output, std::string package = "" ){}
	};
}

#endif
